package metrics;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
		
		
		public aspect DC_Metrics {
		
		
			
		private static int counter = 0; // counter that counts the total number of coupling per the application
		private static double percentage= 0.0; // to find the percentage of coupling per individual class
		final int tabPos = 70;
		private static boolean x,y;
		private Hashtable <String, List<String>> classCouples = new Hashtable <String, List<String>>(); // the hash table
		
		static DataOutputStream dos;
		
		 public StackTraceElement[] stack;
		 public  StackTraceElement root; 
		static private String caller,callee,buffer;
		static private String appName= "SimpleJavaApp";
		
		private static DecimalFormat df = new DecimalFormat("#.##");
		
			pointcut capture() : call (* *..*(..)) && !within(DC_Metrics) && !within(EF_Metrics); 
		
			pointcut excluded() : call(* org.aspectj..*(..)) || call(* java..*(..)); //|| call (* DC_Metrics.*(..));
			
			pointcut Print() : call (* java.lang.System.exit(..)); // capture the application status (running or not)
		
			
		
		before() : capture() && !excluded() 
		{
			 callee = thisJoinPoint.getSignature().getDeclaringTypeName();
			// get the callee name
		//it inspects the thisJoinPointStaticPart and stores which field has been accessed
		//and by which method.
			
			 caller = thisEnclosingJoinPointStaticPart.getSignature().getDeclaringTypeName(); // get the caller name
		//	String caller = thisEnclosingJoinPointStaticPart.getSignature().getName();
			
			if (!caller.equals(callee)) // exclude reference values
			 {
				 x= caller.contains("$");
				 y= callee.contains("$");
				if (!x && !y)
				{
				update(caller, callee); //update the table by the new results
					
					System.out.println("the class name of caller is "+ caller + "------" + "and the class name of the callee is "+ callee);
					System.out.println(counter); 
					//outputResult(); 
				} 
		
			 }
			
		}
		
		before () :Print()
		{
			outputResult();
			
			try {
				outputResult(); // execute the output method
				
				// open the output file 
				String[] command = { "cmd.exe", "/C", "Start", "D:/Experiments/MetricsResults/SimpleJavaApp/"+appName+"_DC_Output.txt"};
				
				//Process child = 
				Runtime.getRuntime().exec(command);
				//child.destroy();
				}
				catch (Exception e){
					System.out.println(e);
				}
			
			System.out.println("total number of coupling =" + counter);
			}
		
		
			/////////////////////////////////////////////////////
		 	///  update the hash-table by the new information////
			/////////////////////////////////////////////////////
		
		private void update (String caller, String callee) // to add the the information to a hashtable
		{
			if (!classCouples.containsKey(callee)) { // check if the the callee is already exist or not 
		   List<String> l = new ArrayList<String>();
		   l.add(caller);
		   classCouples.put(callee, l);
		 		counter += 1;
		
			} else { //just add callee to list of callees
			   List<String> l = classCouples.get(callee);
			   if (!l.contains(caller)) {
			       l.add(caller);
			       classCouples.put(callee, l);
			        counter += 1; // adds 1 to the counter (whenever there is coupling)
		     	
		   }// end of the 2nd if 
		
		}// end of else 
		
		}
		
		
				////////////////////////////////////////////////////
				///          the output display method		    ////
				////////////////////////////////////////////////////
	public void outputResult() {
		System.out.println("\nResult of DCBO Metrics (classname, number of classes coupled to , Classes that are coupled to ):  ");
		System.out.println("-------------------------------------------------------------------------");
		
		//sorted output of hashtable
		Vector<String> v = new Vector<String>(classCouples.keySet());
		Collections.sort(v);
		Iterator<String> it = v.iterator();
		
	
		////
		//writing results into a file ...
		////
		
		
		
		
		// write into a text file 
		
//			stack = Thread.currentThread ().getStackTrace ();
//		    root = stack[stack.length - 1];
//		    buffer= root.getFileName();
//		    appName = buffer.replaceAll("[.java]", "");
		    
		    
			writeToFile("D:/Experiments/MetricsResults/SimpleJavaApp/"+appName+"_DC_Output.txt", "\t\t\t 	"+appName+" Program  \r\n\r\n " , false, true);
	
		//	writeToFile("D:/Experiments/MetricsResults/ATM/ATM_DC_Output.txt", "\t\t\t Total number of coupled classes = " + counter +" \r\n\r\n", true, true);
					
			writeToFile("D:/Experiments/MetricsResults/SimpleJavaApp/"+appName+"_DC_Output.txt", "\t\t\r\r\t     	Results of DC Metrics \r" , true, true);
				
			writeToFile("D:/Experiments/MetricsResults/SimpleJavaApp/"+appName+"_DC_Output.txt", "\r Callee \t\t\t\t\t\t\t #callers  Percentage\r\n"  , true, true);

			writeToFile("D:/Experiments/MetricsResults/SimpleJavaApp/"+appName+"_DC_Output.txt", "\t\t\t\t List of callers\n"  , true, true);
			
			//writeToFile("D:/Experiments/MetricsResults/ATM/ATM_DC_Output.txt", "\t\t\tcallers  Percentage: \r\n" , true, true);
			
			writeToFile("D:/Experiments/MetricsResults/SimpleJavaApp/"+appName+"_DC_Output.txt", "\t\t\t \r\n" + "====================================================================================== \r\n" , true, true);
			
			
			String clsName ;
			String printName ;
			int hashtableCounter;
			hashtableCounter = classCouples.size();
		while (it.hasNext()) {
		   clsName = it.next();
		   printName = pad(clsName, tabPos);
		  
		   System.out.println(printName + " " + classCouples.get(clsName).size());
		   
		   // use to find the percentage of coupling per each class
		   percentage = (double)classCouples.get(clsName).size()/(double)counter;
		   percentage *= 100; 
		   
		   System.out.println("\t\t the presentage of coupling for this class is " + df.format(percentage)+"%");
		   
		 System.out.println("\t\t" + classCouples.get(clsName) + "\n");
		
		 //
		 //writing the output into a text file
		 //
		 
		 
		
		 writeToFile("D:/Experiments/MetricsResults/SimpleJavaApp/"+appName+"_DC_Output.txt", "\r\n "+ printName + "" + classCouples.get(clsName).size() + "\t"+df.format(percentage) +"%\r\n", true, true);
		
		 writeToFile("D:/Experiments/MetricsResults/SimpleJavaApp/"+appName+"_DC_Output.txt", "\n\r\n\r\t\t\t\t " + classCouples.get(clsName).toString()+ "\t\n", true, true);
		 hashtableCounter = hashtableCounter-1;
		 if(it.hasNext())
		 {
		 writeToFile("D:/Experiments/MetricsResults/SimpleJavaApp/"+appName+"_DC_Output.txt", "\r\n--------------------------------------------------------------------------------------", true, true);
		 }

		} // end of the loop 
		
		 writeToFile("D:/Experiments/MetricsResults/SimpleJavaApp/"+appName+"_DC_Output.txt", "\t\t\t \r\n\r\n" + "====================================================================================== \r\n" , true, true);
			
		 writeToFile("D:/Experiments/MetricsResults/SimpleJavaApp/"+appName+"_DC_Output.txt", " \r\r\n\n Total number of coupled classes = " + counter +" \r\n\r\n", true, false);

		
		}
		
		
		// to retrieve data from the hashtable 
		static String pad(final String str, final int width) 
		{
		StringBuffer buf = new StringBuffer(str);
		int space = width - buf.length();
		while (space-- > 0) {
		   buf.append(' ');
		}
		
		return buf.toString();
		
		}
		
		
		// writing the output into a text file
		public boolean writeToFile(String fileName, String dataLine,
			      boolean isAppendMode, boolean isNewLine) {
			    if (isNewLine) {
			      dataLine = "\n" + dataLine;
			    }
		
			    try {
			      File outFile = new File(fileName);
			      if (isAppendMode) {
			        dos = new DataOutputStream(new FileOutputStream(fileName, true));
			      } else {
			        dos = new DataOutputStream(new FileOutputStream(outFile));
			      }
		
			      dos.writeBytes(dataLine);
			      dos.close();
			    } catch (FileNotFoundException ex) {
			      return (false);
			    } catch (IOException ex) {
			      return (false);
			    }
			    return (true);
		
			  }
		
		
		}